package yl.hs.bmipfull.service.system.impl;

import com.google.zxing.qrcode.QRCodeWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import yl.hs.bmipfull.daos.salemanage.ICustomerInfoDao;
import yl.hs.bmipfull.daos.salemanage.ILinkerInfoDao;
import yl.hs.bmipfull.daos.system.ICoreDataTagDao;
import yl.hs.bmipfull.daos.system.IDutyInfoDao;
import yl.hs.bmipfull.daos.system.IUserInfoDao;
import yl.hs.bmipfull.daos.system.IUserLoginDao;
import yl.hs.bmipfull.pojo.salemanage.LinkerInfoEntity;
import yl.hs.bmipfull.pojo.system.*;
import yl.hs.bmipfull.security.MD5;
import yl.hs.bmipfull.service.system.IOrganizationInfoService;
import yl.hs.bmipfull.service.system.IRoleService;
import yl.hs.bmipfull.service.system.IUserService;
import yl.hs.bmipfull.utils.*;
import yl.hs.bmipfull.utils.datastate.ActionType;
import yl.hs.bmipfull.utils.datastate.UserState;
import yl.hs.bmipfull.viewmodels.PageQuery;
import yl.hs.bmipfull.viewmodels.PageResult;
import yl.hs.bmipfull.viewmodels.SubmitModel;

import javax.security.auth.login.AccountException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends SystemBaseService implements IUserService, UserDetailsService {

    @Autowired
    IUserInfoDao userDao;
    @Autowired
    IUserLoginDao userLoginDao;
    @Autowired
    IDutyInfoDao dutyDao;
    @Autowired
    IRoleService roleDao;
    @Autowired
    IOrganizationInfoService orgSvc;
    @Autowired
    ICoreDataTagDao tagDao;

    @Autowired
    ILinkerInfoDao linkerInfoDao;
    @Autowired
    ICustomerInfoDao customerInfoDao;

    public UserInfoEntity getUserByLogin(String account, String password) throws Exception {
        var user = userDao.findByAccount(account);
        if (user != null) {
            if (user.getState() >= 0) {
                var userLogin = userLoginDao.findBySN(user.getSn());
                if (userLogin != null) {
                    if (MD5.getMD5(password).equals(userLogin.getUserPWD())) {
                        return user;
                    } else {
                        throw new AccountException("密码错误！");
                    }
                } else {
                    throw new AccountException("密码错误！");
                }
            } else {
                throw new AccountException("用户账户不可用");
            }
        }
        return null;
    }

    private List<SimpleGrantedAuthority> findPermissions(UserInfoEntity user) {

        List<SimpleGrantedAuthority> list = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("api/home/main"));
        list.add(new SimpleGrantedAuthority("api/home/user"));
        list.add(new SimpleGrantedAuthority("api/home/content"));
        return list;
    }

    @Override
    public UserInfoEntity findUserByAccount(String account) {
        return userDao.findByAccount(account);
    }

    @Override
    public UserInfoEntity findConstructionUserByAccount(String account) {
        var user = linkerInfoDao.findUserByAccount(account);
        if (user != null) {
            var nUser = new UserInfoEntity();
            nUser.setSn(user.getSn());
            nUser.setCompanySN(user.getCustSn());
            var customer = customerInfoDao.findBySN(user.getCustSn());
            if(customer!=null){
                nUser.setOrganizationCode(customer.getOrganizationCode());
            }
            nUser.setAccount(account);
            nUser.setMobileNum(account);
            nUser.setRealname(user.getFullName());
            nUser.setGender(user.isGender());
            nUser.setNickname(customer.getCustName());
            nUser.setQqNum(user.getQq());
            nUser.setEmail(user.getEmail());
            nUser.setState((short)1);
            nUser.setUseLanguage("");
            nUser.setTheme("");
            nUser.setImageSN("");
            return nUser;
        }
        return null;
    }

    @Override
    public UserLoginEntity findUserLoginBySN(String userSN) {

        return userLoginDao.findBySN(userSN);
    }

    public PageResult<Object> queryPage(PageQuery query, int rowNum, int page) {
        Map<String, Object> queryParam = query.getQuery();

        var user= HsApp.getCurrent();
        if(!user.getAccount().equals(HsApp.getSystemSet(CK.SupperAdminKey))){
            queryParam.put("AdminAccount",HsApp.getSystemSet(CK.SupperAdminKey));
        }
        PageResult<Object> result = new PageResult(rowNum);
        result.setRecords(userDao.queryCountByCondition(queryParam));
        result.setPage(page);
        queryParam.put("querySkip", (page - 1) * rowNum);
        queryParam.put("pageSize", rowNum);
        query.checkSortSides(UserInfoEntity.class);

        queryParam.put("sortSide", String.join(",", query.getSortSides()));
        var dataList = userDao.queryListByCondition(queryParam);
        var newList = new ArrayList<Object>();
        if(dataList.size()>0){
            var userSNList = dataList.stream().map(t -> t.getSn()).collect(Collectors.toList()).toArray(new String[dataList.size()]);
            var tags = tagDao.queryBySN(userSNList);
            var orgCode = Tl.getDBMapCode(OrganizationInfoEntity.class);
            var roleCode = Tl.getDBMapCode(RoleInfoEntity.class);
            var dutyCode = Tl.getDBMapCode(DutyInfoEntity.class);
            dataList.forEach(t -> {
                var map = t.toMap();

                var roles = tags.stream().filter(p -> p.getTagType().equals(roleCode) && p.getMainSN().equals(t.getSn())).map(p -> p.getTagSN()).collect(Collectors.toList());
                map.put("roleSN", String.join(",", roles));

                var duties = tags.stream().filter(p -> p.getTagType().equals(dutyCode) && p.getMainSN().equals(t.getSn())).map(p -> p.getTagSN()).collect(Collectors.toList());
                map.put("dutySN", String.join(",", duties));
                var units = tags.stream().filter(p -> p.getTagType().equals(orgCode) && p.getMainSN().equals(t.getSn())).map(p -> p.getTagSN()).collect(Collectors.toList());
                map.put("unitSN", String.join(",", units));
                newList.add(map);
            });
        }
        result.setData(newList);
        return result;
    }

    public UserInfoEntity findUserBySN(String sn) {
        return userDao.findBySN(sn);
    }

    @Transactional
    @Override
    public Integer deleteUser(String[] sn) {
        var result = userDao.updateUserStateToDeleteByUserSN(sn, DataState.DELETED.code);
        if (result > 0) {
            tagDao.deleteTagByTagSN(sn);
            writeLog(ActionType.DELETE, sn, UserInfoEntity.class);
        }
        return result;
    }

    public Integer updatePersonalInfo(SubmitModel model){
        int result=0;
        try {
            var user = findUserBySN(model.getSn());
            Tl.getChangeLog(user, model.getData());
            user.setPYFieldValue();
            result= userDao.update(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    @Transactional
    public Integer save(SubmitModel model) throws Exception {
        int result = 0;
        try {
            if (Tl.isEmpty(model.getSn())) {
                var newSn = queryNewSN(UserInfoEntity.class);
                var user = new UserInfoEntity();
                UserLoginEntity userLoginEntity = new UserLoginEntity();
                userLoginEntity.setUserSN(newSn);
                userLoginEntity.setUserPWD(MD5.getMD5(model.getData().get("userPWD").toString()));

                userLoginDao.insertNew(userLoginEntity);
                Tl.getChangeLog(user, model.getData());
                user.setSn(newSn);
                user.setPYFieldValue();
                var userCode = saveUserTags(model.getData(), user.getSn());
                user.setOrganizationCode(userCode);
                var company = orgSvc.findCompany(userCode.split(","));
                if(company!=null) {
                    user.setCompanySN(company.getSn());
                }
                else
                {
                    user.setCompanySN(userCode.split(",")[0]);
                }
                result = userDao.insertNew(user);
                writeLog(ActionType.CREATE, newSn, "", "", UserInfoEntity.class);
            } else {
                var user = findUserBySN(model.getSn());
                var backJson = user.toJson();
                var changeLog = Tl.getChangeLog(user, model.getData());
                user.setPYFieldValue();
                var userCode = saveUserTags(model.getData(), user.getSn());
                user.setOrganizationCode(userCode);
                var company = orgSvc.findCompany(userCode.split(","));
                if(company!=null) {
                    user.setCompanySN(company.getSn());
                }
                else
                {
                    user.setCompanySN(userCode.split(",")[0]);
                }
                result = userDao.update(user);
                if (result > 0) {
                    tagDao.updateTag(user.getSn(), user.getRealname());
                    writeLog(ActionType.UPDATE, model.getSn(), changeLog, backJson, UserInfoEntity.class);
                }
            }
        } catch (Exception ee) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw ee;
        }
        return result;
    }

    public String saveUserTags(Map<String, Object> data, String mainSN) {
        var roleObj = data.get("roleSN");
        if (roleObj != null) {
            var allRoles = roleDao.queryAll();
            var roleSNList = Arrays.stream(roleObj.toString().split(",")).collect(Collectors.toList());
            var roleList = allRoles.stream().filter(t -> roleSNList.contains(t.getSn())).collect(Collectors.toList());
            var roleCode = Tl.getDBMapCode(RoleInfoEntity.class);
            tagDao.deleteByMainSn(mainSN, roleCode);
            for (var t : roleList) {
                tagDao.insertNew(new CoreDataTagEntity(mainSN, t.getRoleName(), roleCode, t.getSn()));
            }
        }
        var dutyObj = data.get("dutySN");
        if (dutyObj != null) {
            var allDuty = dutyDao.queryAll();
            var dutySNList = Arrays.stream(dutyObj.toString().split(",")).collect(Collectors.toList());
            var dutyList = allDuty.stream().filter(t -> dutySNList.contains(t.getSn())).collect(Collectors.toList());
            var dutyCode = Tl.getDBMapCode(DutyInfoEntity.class);
            tagDao.deleteByMainSn(mainSN, dutyCode);
            for (var t : dutyList) {
                tagDao.insertNew(new CoreDataTagEntity(mainSN, t.getDutyName(), dutyCode, t.getSn()));
            }
        }
        var unitCodeList = new ArrayList<String>();
        var unitObj = data.get("unitSN");
        if (unitObj != null) {
            var allUnits = orgSvc.queryListByFullNameKey(null);
            var unitSNList = Arrays.stream(unitObj.toString().split(",")).collect(Collectors.toList());
            var unitList = allUnits.stream().filter(t -> unitSNList.contains(t.getSn())).collect(Collectors.toList());
            var unitCode = Tl.getDBMapCode(OrganizationInfoEntity.class);
            tagDao.deleteByMainSn(mainSN, unitCode);
            for (var t : unitList) {
                tagDao.insertNew(new CoreDataTagEntity(mainSN, t.getFullName(), unitCode, t.getSn()));
                var codes = Tl.isEmpty(t.getParentCode()) ? t.getSn().split(",") : t.getParentCode().split(",");
                for (var item : codes) {
                    if (!unitCodeList.contains(item)) {
                        unitCodeList.add(item);
                    }
                }
            }
        }
        return String.join(",", unitCodeList);
    }

    @Transactional
    public Integer changePassword(String sn, String oldPwd, String newPwd) throws AccountException {
        var login = userLoginDao.findBySN(sn);
        if (MD5.getMD5(oldPwd).equals(login.getUserPWD())) {
            login.setUserPWD(MD5.getMD5(newPwd));
            return userLoginDao.update(login);
        } else {
            throw new AccountException("密码错误");
        }
    }

    public List<UserInfoEntity> queryAll() {
        var condition = String.format(" state>=%d", DataState.CREATED.code);
        var user=HsApp.getCurrent();
        var superAdmin=HsApp.getSystemSet(CK.SupperAdminKey);
        if(!user.getAccount().equals(superAdmin)){
            condition +=String.format(" and account!='"+superAdmin+"'");
        }
        return userDao.queryByCondition(condition);
    }

    @Override
    public List<UserInfoEntity> queryAlByOrganizationCode(String organizationCode) {
        return userDao.queryAlByOrganizationCode(organizationCode);
    }

    @Override
    public List<UserInfoEntity> queryByUserSNList(List<String> snList) {
        return userDao.queryByUserSNList(snList);
    }

    @Override
    public Integer RestPassword(String sn) {
        var login = userLoginDao.findBySN(sn);
        login.setUserPWD(MD5.getMD5(MD5.getMD5("0000")));
        return userLoginDao.update(login);
    }

    @Override
    public Integer updateUserInfo(UserInfoEntity user) {
        return userDao.update(user);
    }

    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
        var loginUrl = HsApp.getRequest().getRequestURL();
        if(loginUrl.indexOf(CK.Construction)>-1) {
            var user = linkerInfoDao.findUserByAccount(account);
            if(user==null){
                throw new UsernameNotFoundException(String.format("没有%s这个用户", account));
            }
            return new User(account, user.getPassword(), user.getState()==1,
                    true, true, true
                    , findPermissions(user));
        }
        else {
            var user = userDao.findByAccount(account);
            if (user == null) {
                throw new UsernameNotFoundException(String.format("没有%s这个用户", account));
            }
            var userLogin = userLoginDao.findBySN(user.getSn());
            var state = UserState.parseState(user.getState());
            return new User(account, userLogin.getUserPWD(), state == UserState.Enabled,
                    state != UserState.Expired, true, state != UserState.Locked
                    , findPermissions(user));
        }
        //return null 即认证失败
    }

    private List<SimpleGrantedAuthority> findPermissions(LinkerInfoEntity user){
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("api/construction/main"));
        list.add(new SimpleGrantedAuthority("api/construction/user"));
        list.add(new SimpleGrantedAuthority("api/construction/content"));
        return list;
    }
}
